// const base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// const base64DecodeChars = new Array(
// 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
// 	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
// 	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
// 	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
// 	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
// 	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);

// const base64encode = function(str) {
// 	var out, i, len;
// 	var c1, c2, c3;
// 	len = str.length;
// 	i = 0;
// 	out = "";
// 	while (i < len) {
// 		c1 = str.charCodeAt(i++) & 0xff;
// 		if (i == len) {
// 			out += base64EncodeChars.charAt(c1 >> 2);
// 			out += base64EncodeChars.charAt((c1 & 0x3) << 4);
// 			out += "==";
// 			break;
// 		}
// 		c2 = str.charCodeAt(i++);
// 		if (i == len) {
// 			out += base64EncodeChars.charAt(c1 >> 2);
// 			out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
// 			out += base64EncodeChars.charAt((c2 & 0xF) << 2);
// 			out += "=";
// 			break;
// 		}
// 		c3 = str.charCodeAt(i++);
// 		out += base64EncodeChars.charAt(c1 >> 2);
// 		out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
// 		out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
// 		out += base64EncodeChars.charAt(c3 & 0x3F);
// 	}
// 	return out;
// }

// const base64decode = function(str) {
// 	var c1, c2, c3, c4;
// 	var i, len, out;
// 	len = str.length;
// 	i = 0;
// 	out = "";
// 	while (i < len) {
// 		/* c1 */
// 		do {
// 			c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
// 		} while (i < len && c1 == -1);
// 		if (c1 == -1)
// 			break;
// 		/* c2 */
// 		do {
// 			c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
// 		} while (i < len && c2 == -1);
// 		if (c2 == -1)
// 			break;
// 		out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
// 		/* c3 */
// 		do {
// 			c3 = str.charCodeAt(i++) & 0xff;
// 			if (c3 == 61)
// 				return out;
// 			c3 = base64DecodeChars[c3];
// 		} while (i < len && c3 == -1);
// 		if (c3 == -1)
// 			break;
// 		out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
// 		/* c4 */
// 		do {
// 			c4 = str.charCodeAt(i++) & 0xff;
// 			if (c4 == 61)
// 				return out;
// 			c4 = base64DecodeChars[c4];
// 		} while (i < len && c4 == -1);
// 		if (c4 == -1)
// 			break;
// 		out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
// 	}
// 	return out;
// }

// const utf16to8=function(str){
// 	var out, i, len, c;
// 	　　out = "";
// 	　　len = str.length;
// 	　　for(i = 0; i < len; i++) {
// 	 c = str.charCodeAt(i);
// 	 if ((c >= 0x0001) && (c <= 0x007F)) {
// 	　　 out += str.charAt(i);
// 	 } else if (c > 0x07FF) {
// 	　　 out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
// 	　　 out += String.fromCharCode(0x80 | ((c >>　6) & 0x3F));
// 	　　 out += String.fromCharCode(0x80 | ((c >>　0) & 0x3F));
// 	 } else {
// 	　　 out += String.fromCharCode(0xC0 | ((c >>　6) & 0x1F));
// 	　　 out += String.fromCharCode(0x80 | ((c >>　0) & 0x3F));
// 	 }
// 	　　}
// 	　　return out;
// }
// const  utf8to16=function(str){
// 	　var out, i, len, c;
// 	　　var char2, char3;
// 	　　out = "";
// 	　　len = str.length;
// 	　　i = 0;
// 	　　while(i < len) {
// 	 c = str.charCodeAt(i++);
// 	 switch(c >> 4)
// 	 {
// 	　 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 	　　 // 0xxxxxxx
// 	　　 out += str.charAt(i-1);
// 	　　 break;
// 	　 case 12: case 13:
// 	　　 // 110x xxxx　 10xx xxxx
// 	　　 char2 = str.charCodeAt(i++);
// 	　　 out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
// 	　　 break;
// 	　 case 14:
// 	　　 // 1110 xxxx　10xx xxxx　10xx xxxx
// 	　　 char2 = str.charCodeAt(i++);
// 	　　 char3 = str.charCodeAt(i++);
// 	　　 out += String.fromCharCode(((c & 0x0F) << 12) |
// 	　　　　((char2 & 0x3F) << 6) |
// 	　　　　((char3 & 0x3F) << 0));
// 	　　 break;
// 	 }
// 	　　}
// 	　　return out;
// }


let _PADCHAR = "=",
	_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
	_VERSION = "1.1"; //Mr. Ruan fix to 1.1 to support asian char(utf8)
function _getbyte64(s, i) {
	// This is oddly fast, except on Chrome/V8.
	// Minimal or no improvement in performance by using a
	// object with properties mapping chars to value (eg. 'A': 0)

	var idx = _ALPHA.indexOf(s.charAt(i));

	if (idx === -1) {
		throw "Cannot decode base64";
	}

	return idx;
}

function _decode_chars(y, x) {
	while (y.length > 0) {
		var ch = y[0];
		if (ch < 0x80) {
			y.shift();
			x.push(String.fromCharCode(ch));
		} else if ((ch & 0x80) == 0xc0) {
			if (y.length < 2) break;
			ch = y.shift();
			var ch1 = y.shift();
			x.push(String.fromCharCode(((ch & 0x1f) << 6) + (ch1 & 0x3f)));
		} else {
			if (y.length < 3) break;
			ch = y.shift();
			var ch1 = y.shift();
			var ch2 = y.shift();
			x.push(String.fromCharCode(((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f)));
		}
	}
}

const _decode=function (s) {
	var pads = 0,
		i,
		b10,
		imax = s.length,
		x = [],
		y = [];

	s = String(s);

	if (imax === 0) {
		return s;
	}

	if (imax % 4 !== 0) {
		throw "Cannot decode base64";
	}

	if (s.charAt(imax - 1) === _PADCHAR) {
		pads = 1;

		if (s.charAt(imax - 2) === _PADCHAR) {
			pads = 2;
		}

		// either way, we want to ignore this last block
		imax -= 4;
	}

	for (i = 0; i < imax; i += 4) {
		var ch1 = _getbyte64(s, i);
		var ch2 = _getbyte64(s, i + 1);
		var ch3 = _getbyte64(s, i + 2);
		var ch4 = _getbyte64(s, i + 3);

		b10 = (_getbyte64(s, i) << 18) | (_getbyte64(s, i + 1) << 12) | (_getbyte64(s, i + 2) << 6) | _getbyte64(s, i +
			3);
		y.push(b10 >> 16);
		y.push((b10 >> 8) & 0xff);
		y.push(b10 & 0xff);
		_decode_chars(y, x);
	}
	switch (pads) {
		case 1:
			b10 = (_getbyte64(s, i) << 18) | (_getbyte64(s, i + 1) << 12) | (_getbyte64(s, i + 2) << 6);
			y.push(b10 >> 16);
			y.push((b10 >> 8) & 0xff);
			break;

		case 2:
			b10 = (_getbyte64(s, i) << 18) | (_getbyte64(s, i + 1) << 12);
			y.push(b10 >> 16);
			break;
	}
	_decode_chars(y, x);
	if (y.length > 0) throw "Cannot decode base64";
	return x.join("");
}


function _get_chars(ch, y) {
	if (ch < 0x80) y.push(ch);
	else if (ch < 0x800) {
		y.push(0xc0 + ((ch >> 6) & 0x1f));
		y.push(0x80 + (ch & 0x3f));
	} else {
		y.push(0xe0 + ((ch >> 12) & 0xf));
		y.push(0x80 + ((ch >> 6) & 0x3f));
		y.push(0x80 + (ch & 0x3f));
	}
}



const _encode= function (s) {
	if (arguments.length !== 1) {
		throw "SyntaxError: exactly one argument required";
	}

	s = String(s);
	if (s.length === 0) {
		return s;
	}

	//s = _encode_utf8(s);
	var i,
		b10,
		y = [],
		x = [],
		len = s.length;
	i = 0;
	while (i < len) {
		_get_chars(s.charCodeAt(i), y);
		while (y.length >= 3) {
			var ch1 = y.shift();
			var ch2 = y.shift();
			var ch3 = y.shift();
			b10 = (ch1 << 16) | (ch2 << 8) | ch3;
			x.push(_ALPHA.charAt(b10 >> 18));
			x.push(_ALPHA.charAt((b10 >> 12) & 0x3F));
			x.push(_ALPHA.charAt((b10 >> 6) & 0x3f));
			x.push(_ALPHA.charAt(b10 & 0x3f));
		}
		i++;
	}


	switch (y.length) {
		case 1:
			var ch = y.shift();
			b10 = ch << 16;
			x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _PADCHAR + _PADCHAR);
			break;

		case 2:
			var ch1 = y.shift();
			var ch2 = y.shift();
			b10 = (ch1 << 16) | (ch2 << 8);
			x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _ALPHA.charAt((b10 >> 6) & 0x3f) +
				_PADCHAR);
			break;
	}

	return x.join("");
}

export default {
	// base64encode,
	// base64decode,
	// utf16to8,
	// utf8to16
	_decode,
	_encode
}
